package net.cyue.web.easyquery.core.config.json;

import com.fasterxml.jackson.databind.JsonNode;
import net.cyue.util.IdentifierStringConverter;
import net.cyue.util.StringUtil;
import net.cyue.web.easyquery.core.config.ConfigException;
import net.cyue.web.easyquery.core.config.ConfigItemValue;
import net.cyue.web.easyquery.core.db.QueryType;
import net.cyue.web.easyquery.core.db.SQLType;
import net.cyue.web.easyquery.core.http.HTTPRequestMethod;
import net.cyue.web.easyquery.core.http.data.QueryParameter;
import net.cyue.web.easyquery.core.util.JSONUtil;

import java.util.ArrayList;
import java.util.List;

public class JSONConfigItemValue extends ConfigItemValue {

    public static ConfigItemValue fromNode(JsonNode node)
        throws ConfigException
    {
        if (!node.isObject()) {
            throw new ConfigException("Invalid config item: " + node);
        }
        Type type = Type.valueOf(
            JSONUtil
                .getString(node, "type", Type.ADDITIONAL.name())
                .toUpperCase()
        );
        if (type.equals(Type.API)) {
            List<String> pathList = new ArrayList<>();
            JsonNode pathNode = JSONUtil.getNode(node, "path");
            if (pathNode == null) {
                pathNode = JSONUtil.getNode(node, "paths");
            }
            if (pathNode == null) {
                throw new ConfigException("Invalid API config item: " + node);
            } else if (pathNode.isTextual()) {
                pathList.add(pathNode.asText());
            } else if (pathNode.isArray()) {
                for (JsonNode pn : pathNode) {
                    pathList.add(pn.asText());
                }
            }
            if (pathList.isEmpty()) {
                throw new ConfigException("Invalid API config item: " + node);
            }


            API api = new API();
            api.type = Type.API;
            api.description = JSONUtil.getString(node, "description", null);
            api.paths = pathList.toArray(new String[0]);
            api.sql = JSONUtil.getString(node, "sql", null);
            String preHandlerClassName = JSONUtil.getString(node, "prehandler", JSONUtil.getString(node, "preHandler"));
            if (!StringUtil.isBlank(preHandlerClassName)) {
                api.setPreHandler(preHandlerClassName);
            }
            try {
                api.sqlType = SQLType.valueOf(
                    IdentifierStringConverter.camelCase2Snake(
                        JSONUtil.getString(node, "sqlType", SQLType.UNKNOWN.name())
                    ).toUpperCase()
                );
            } catch (IllegalArgumentException e) {
                LOGGER.warn(e.getMessage());
                api.sqlType = SQLType.UNKNOWN;
            }
            try {
                api.queryType = QueryType.valueOf(
                    IdentifierStringConverter.camelCase2Snake(
                        JSONUtil.getString(node, "queryType", QueryType.QUERY_LIST.name())
                    ).toUpperCase()
                );
            } catch (IllegalArgumentException e) {
                LOGGER.warn(e.getMessage());
                api.queryType = QueryType.QUERY_LIST;
            }

            // 请求方法
            String defaultMethodName = HTTPRequestMethod.GET.name();
            if (api.sqlType == SQLType.UPDATE) {
                defaultMethodName = HTTPRequestMethod.POST.name();
            }
            api.method = HTTPRequestMethod.valueOf(
                JSONUtil
                    .getString(node, "method", defaultMethodName)
                    .toUpperCase()
            );

            api.pageParameter = JSONUtil.getString(
                node,
                "pageParameter",
                JSONUtil.getString(node, "page", DEFAULT_PAGE_PARAMETER)
            );
            api.pageSizeParameter = JSONUtil.getString(
                node,
                "pageSizeParameter",
                JSONUtil.getString(node, "pageSize", DEFAULT_PAGE_SIZE_PARAMETER)
            );

            // 分页查询 默认需要 page 与 pageSize 参数
            if (api.queryType == QueryType.QUERY_PAGE || api.queryType == QueryType.PAGE) {
                api.addQueryParameter(
                    new QueryParameter(
                        api.pageParameter,
                        QueryParameter.Type.INT,
                        "页数",
                        true,
                        1
                    )
                );
                api.addQueryParameter(
                    new QueryParameter(
                        api.pageSizeParameter,
                        QueryParameter.Type.INT,
                        "每页数量",
                        true,
                        DEFAULT_PAGE_SIZE
                    )
                );
            }

            // 查询参数
            String parametersFieldName;
            if (node.has("parameters")) {
                parametersFieldName = "parameters";
            } else if (node.has("parameter")) {
                parametersFieldName = "parameter";
            } else if (node.has("queryParameters")) {
                parametersFieldName = "queryParameters";
            } else if (node.has("queryParameter")) {
                parametersFieldName = "queryParameter";
            } else {
                return api;
            }

            JsonNode parametersNode = node.get(parametersFieldName);
            if (!parametersNode.isArray()) {
                throw new ConfigException("Invalid API config item: " + node);
            }
            for (JsonNode parameterNode : parametersNode) {
                QueryParameter parameter = parseParameterNode(parameterNode);
                api.removeQueryParameter(parameter);
                api.addQueryParameter(parameter);
            }

            return api;
        } else {
            Additional additional = new Additional();
            if (!node.has("name") && !node.has("key")) {
                throw new ConfigException("Invalid additional config item: " + node);
            }
            additional.type = Type.ADDITIONAL;
            additional.description = JSONUtil.getString(node, "description", null);
            if (node.has("key") && node.get("key").isTextual()) {
                additional.setKey(JSONUtil.getString(node, "key", null));
            } else {
                additional.group = JSONUtil.getString(node, "group", null);
                additional.name = JSONUtil.getString(node, "name", null);
            }
            additional.value = JSONUtil.getString(node, "value", null);
            return additional;
        }
    }

    private static QueryParameter parseParameterNode(JsonNode node) throws ConfigException {
        if (node.isTextual()) {
            return new QueryParameter(node.asText());
        } else if (node.isObject()) {
            String name = JSONUtil.getString(node, "name");
            String description = JSONUtil.getString(node, "description", null);
            boolean required = JSONUtil.getBoolean(node, "required", false);
            QueryParameter.Type parameterType =
                QueryParameter.Type.valueOf(
                    JSONUtil
                        .getString(node, "type", QueryParameter.Type.ANY.name())
                        .toUpperCase()
                );
            Object defaultValue = null;
            // 获取 defaultValue
            if (
                parameterType.equals(QueryParameter.Type.ANY) ||
                parameterType.equals(QueryParameter.Type.STRING)
            ) {
                defaultValue =
                    JSONUtil.getString(
                        node,
                        "default",
                        JSONUtil.getString(
                            node,
                            "defaultValue",
                            null
                        )
                    );
            } else if (
                parameterType.equals(QueryParameter.Type.INT) ||
                parameterType.equals(QueryParameter.Type.INTEGER) ||
                parameterType.equals(QueryParameter.Type.LONG) ||
                parameterType.equals(QueryParameter.Type.FLOAT) ||
                parameterType.equals(QueryParameter.Type.DOUBLE) ||
                parameterType.equals(QueryParameter.Type.NUMBER)
            ) {
                defaultValue = JSONUtil.getNumber(
                    node,
                    "default",
                    JSONUtil.getNumber(
                        node,
                        "defaultValue",
                        null
                    )
                );
            } else if (
                parameterType.equals(QueryParameter.Type.BOOL) ||
                parameterType.equals(QueryParameter.Type.BOOLEAN)
            ) {
                defaultValue = JSONUtil.getBoolean(
                    node,
                    "default",
                    JSONUtil.getBoolean(
                        node,
                        "defaultValue",
                        null
                    )
                );
            }
            return new QueryParameter(name, parameterType, description, required, defaultValue);
        } else {
            throw new ConfigException("Invalid API config item: " + node);
        }
    }
}
